﻿using AgileLabs.Dapper.FluentMap.Resolver;
using Dapper.FluentMap;
using Dapper.FluentMap.Mapping;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AgileLabs.Dapper.FluentMap;
using AgileLabs.Dapper.FluentMap.Mapping;
using FocusCareer.Entities;

namespace FoucsCareer.EntitiyMap.Dapper
{
    public class PropertyResolver : AgilePropertyResolver
    {
        protected override IEnumerable<PropertyInfo> FilterComplexTypes(IEnumerable<PropertyInfo> properties)
        {
            foreach (var propertyInfo in properties)
            {
                var type = propertyInfo.PropertyType;
                type = Nullable.GetUnderlyingType(type) ?? type;
                type = type.IsArray ? type.GetElementType() : type;

                if (type.GetTypeInfo().IsPrimitive || type.GetTypeInfo().IsEnum || PrimitiveTypes.Contains(type))
                {
                    yield return propertyInfo;
                }
            }
        }
    }

    public class TableNameResolver : AgileTableNameResolver
    {

    }

    public class KeyPropertyResolver : AgileMapper.IKeyPropertyResolver
    {
        /// <inheritdoc/>
        public PropertyInfo ResolveKeyProperty(Type type)
        {
            bool isIdentity;
            return ResolveKeyProperty(type, out isIdentity);
        }

        /// <inheritdoc/>
        public PropertyInfo ResolveKeyProperty(Type type, out bool isIdentity)
        {
            IEntityMap entityMap;
            if (!FluentMapper.EntityMaps.TryGetValue(type, out entityMap))
            {
                return AgileMapper.Resolvers.Default.KeyPropertyResolver.ResolveKeyProperty(type, out isIdentity);
            }

            var mapping = entityMap as IAgileEntityMap;
            if (mapping != null)
            {
                var keyPropertyMaps = entityMap.PropertyMaps.OfType<AgilePropertyMap>().Where(e => e.Key).ToList();

                if (keyPropertyMaps.Count == 1)
                {
                    var keyPropertyMap = keyPropertyMaps[0];
                    isIdentity = keyPropertyMap.Identity;
                    return keyPropertyMap.PropertyInfo;
                }
                else
                {
                    var keyProperty = typeof(EntityBase).GetProperty(nameof(EntityBase.Id));
                    isIdentity = true;
                    return keyProperty;
                }

                if (keyPropertyMaps.Count > 1)
                {
                    var msg = string.Format("Found multiple key properties on type '{0}'. This is not yet supported. The following key properties were found:{1}{2}",
                                            type.FullName,
                                            Environment.NewLine,
                                            string.Join(Environment.NewLine, keyPropertyMaps.Select(t => t.PropertyInfo.Name)));

                    throw new Exception(msg);
                }
            }

            // Fall back to the default mapping strategy.
            return AgileMapper.Resolvers.Default.KeyPropertyResolver.ResolveKeyProperty(type, out isIdentity);
        }

    }

    /// <summary>
    /// Implements the <see cref="AgileMapper.IColumnNameResolver"/> interface by using the configured mapping.
    /// </summary>
    public class ColumnNameResolver : AgileMapper.IColumnNameResolver
    {
        /// <inheritdoc/>
        public string ResolveColumnName(PropertyInfo propertyInfo)
        {
            if (propertyInfo.DeclaringType != null)
            {
                IEntityMap entityMap;
                if (FluentMapper.EntityMaps.TryGetValue(propertyInfo.ReflectedType, out entityMap))
                {
                    var mapping = entityMap as IAgileEntityMap;
                    if (mapping != null)
                    {
                        var propertyMaps = entityMap.PropertyMaps.Where(m => m.PropertyInfo.Name == propertyInfo.Name).ToList();
                        if (propertyMaps.Count == 1)
                        {
                            return propertyMaps[0].ColumnName;
                        }
                    }
                }
            }

            var columnName = AgileMapper.Resolvers.Default.ColumnNameResolver.ResolveColumnName(propertyInfo);
            return columnName;
        }
    }
}
